﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;

public static class InputExecuteEvents {

	public delegate void EventFunction<T1>(T1 handler, InputEventData eventData);

	private static readonly EventFunction<IInputEnterHandler> s_InputEnterHandler = new EventFunction<IInputEnterHandler> (Execute);

	private static readonly EventFunction<IInputExitHandler> s_InputExitHandler = new EventFunction<IInputExitHandler> (Execute);

	private static readonly EventFunction<IInputDownHandler> s_InputDownHandler = new EventFunction<IInputDownHandler> (Execute);

	private static readonly EventFunction<IInputUpHandler> s_InputUpHandler = new EventFunction<IInputUpHandler> (Execute);

	private static readonly EventFunction<IInputClickHandler> s_InputClickHandler = new EventFunction<IInputClickHandler> (Execute);

	private static readonly EventFunction<IInputBeginDragHandler> s_InputBeginDragHandler = new EventFunction<IInputBeginDragHandler> (Execute);

	private static readonly EventFunction<IInputDragHandler> s_InputDragHandler = new EventFunction<IInputDragHandler> (Execute);

	private static readonly EventFunction<IInputEndDragHandler> s_InputEndDragHandler = new EventFunction<IInputEndDragHandler> (Execute);

	private static readonly ObjectPool<List<IInputEventHandler>> s_HandlerListPool = new ObjectPool<List<IInputEventHandler>>(null, delegate(List<IInputEventHandler> l)
	{
		l.Clear();
	});

	private static readonly List<Transform> s_InternalTransformList = new List<Transform> (30);

	public static EventFunction<IInputEnterHandler> InputEnterHandler
	{
		get
		{
			return s_InputEnterHandler;
		}
	}
	public static EventFunction<IInputExitHandler> InputExitHandler
	{
		get
		{
			return s_InputExitHandler;
		}
	}
	public static EventFunction<IInputDownHandler> InputDownHandler
	{
		get
		{
			return s_InputDownHandler;
		}
	}
	public static EventFunction<IInputUpHandler> InputUpHandler
	{
		get
		{
			return s_InputUpHandler;
		}
	}
	public static EventFunction<IInputClickHandler> InputClickHandler
	{
		get
		{
			return s_InputClickHandler;
		}
	}
	public static EventFunction<IInputBeginDragHandler> InputBeginDragHandler
	{
		get
		{
			return s_InputBeginDragHandler;
		}
	}
	public static EventFunction<IInputDragHandler> InputDragHandler
	{
		get
		{
			return s_InputDragHandler;
		}
	}
	public static EventFunction<IInputEndDragHandler> InputEndDragHandler
	{
		get
		{
			return s_InputEndDragHandler;
		}
	}

	private static void Execute(IInputEnterHandler handler, InputEventData eventData)
	{
		handler.OnInputEnter(ValidateEventData<InputEventData>(eventData));
	}

	private static void Execute(IInputExitHandler handler, InputEventData eventData)
	{
		handler.OnInputExit(ValidateEventData<InputEventData>(eventData));
	}

	private static void Execute(IInputDownHandler handler, InputEventData eventData)
	{
		handler.OnInputDown(ValidateEventData<InputEventData>(eventData));
	}

	private static void Execute(IInputUpHandler handler, InputEventData eventData)
	{
		handler.OnInputUp(ValidateEventData<InputEventData>(eventData));
	}

	private static void Execute(IInputClickHandler handler, InputEventData eventData)
	{
		handler.OnInputClick(ValidateEventData<InputEventData>(eventData));
	}

	private static void Execute(IInputBeginDragHandler handler, InputEventData eventData)
	{
		handler.OnInputBeginDrag(ValidateEventData<InputEventData>(eventData));
	}

	private static void Execute(IInputDragHandler handler, InputEventData eventData)
	{
		handler.OnInputDrag(ValidateEventData<InputEventData>(eventData));
	}

	private static void Execute(IInputEndDragHandler handler, InputEventData eventData)
	{
		handler.OnInputEndDrag(ValidateEventData<InputEventData>(eventData));
	}

	public static T ValidateEventData<T>(InputEventData data) where T : class
	{
		if (!(data is T))
		{
			throw new ArgumentException(string.Format("Invalid type: {0} passed to event expecting {1}", data.GetType(), typeof(T)));
		}
		return data as T;
	}

	/// <summary>
	/// 取所有父类的event
	/// </summary>
	/// <param name="root">Root.</param>
	/// <param name="eventChain">Event chain.</param>
	private static void GetEventChain(GameObject root, IList<Transform> eventChain)
	{
		eventChain.Clear();
		if (root != null)
		{
			Transform transform = root.transform;
			while (transform != null)
			{
				eventChain.Add(transform);
				transform = transform.parent;
			}
		}
	}

	public static bool Execute<T>(GameObject target, InputEventData eventData, EventFunction<T> functor) where T : IInputEventHandler
	{
		List<IInputEventHandler> list = s_HandlerListPool.Get();
		GetEventList<T>(target, list);
		int i = 0;
		while (i < list.Count)
		{
			T handler;
			try
			{
				handler = (T)((object)list[i]);
			}
			catch (Exception innerException)
			{
				IInputEventHandler eventSystemHandler = list[i];
				Debug.LogException(new Exception(string.Format("Type {0} expected {1} received.", typeof(T).Name, eventSystemHandler.GetType().Name), innerException));
				i++;
				continue;
			}
			try
			{
				functor(handler, eventData);
			}
			catch (Exception exception)
			{
				Debug.LogException(exception);
			}
			i++;
			continue;
		}
		int count = list.Count;
		s_HandlerListPool.Release(list);
		return count > 0;
	}

	/// <summary>
	/// 如果当前对象没有event，取父对象找对应事件
	/// </summary>
	/// <returns>The hierarchy.</returns>
	/// <param name="root">Root.</param>
	/// <param name="eventData">Event data.</param>
	/// <param name="callbackFunction">Callback function.</param>
	/// <typeparam name="T">The 1st type parameter.</typeparam>
	public static GameObject ExecuteHierarchy<T>(GameObject root, InputEventData eventData, EventFunction<T> callbackFunction) where T : IInputEventHandler
	{
		GetEventChain(root, s_InternalTransformList);
		GameObject result;
		for (int i = 0; i < s_InternalTransformList.Count; i++)
		{
			Transform transform = s_InternalTransformList[i];
			if (Execute<T>(transform.gameObject, eventData, callbackFunction))
			{
				result = transform.gameObject;
				return result;
			}
		}
		result = null;
		return result;
	}


	private static void GetEventList<T>(GameObject go, IList<IInputEventHandler> results) where T : IInputEventHandler
	{
		if (results == null)
		{
			throw new ArgumentException("Results array is null", "results");
		}
		if (go != null && go.activeInHierarchy)
		{
			List<Component> list = ListPool<Component>.Get();
			go.GetComponents<Component>(list); //去对象上所有的监听
			for (int i = 0; i < list.Count; i++)
			{
				if (ShouldSendToComponent<T>(list[i]))
				{
					results.Add(list[i] as IInputEventHandler);
				}
			}
			ListPool<Component>.Release(list);
		}
	}

	private static bool ShouldSendToComponent<T>(Component component) where T : IInputEventHandler
	{
		if (component is T) {
			Behaviour behaviour = component as Behaviour;
			if (behaviour != null && behaviour.isActiveAndEnabled) {
				return true;
			}
		}
		return false;
	}

	public static bool CanHandleEvent<T>(GameObject go) where T : IInputEventHandler
	{
		List<IInputEventHandler> list = s_HandlerListPool.Get();
		GetEventList<T>(go, list);
		int count = list.Count;
		s_HandlerListPool.Release(list);
		return count != 0;
	}

	public static GameObject GetEventHandler<T>(GameObject root) where T : IInputEventHandler
	{
		GameObject result;
		if (root == null)
		{
			result = null;
		}
		else
		{
			Transform transform = root.transform;
			while (transform != null)
			{
				if (CanHandleEvent<T>(transform.gameObject))
				{
					result = transform.gameObject;
					return result;
				}
				transform = transform.parent;
			}
			result = null;
		}
		return result;
	}
}
